/*
 * Copyright (c) 2013 Tob
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package de.tudarmstadt.kom.androidsensorapi.datarepresentation.location;

import android.location.Location;

/**
 * The Location is a internal representation for a Location received by location-Sensors.
 * It has the world location, the accuracy and the speed measured by the SensorInterface
 *
 * Created by Tobias Welther
 */
public class SimpleLocation {

    /**
     * The Latitude of the Point
     */
    private final double latitude;

    /**
     * The Longitude of the Point
     */
    private final double longitude;

    /**
     * The height of this point in relation to Ocean height.
     * This is not mandatory. A non existing height is always Double.MIN_VALUE
     */
    private final double height;

    /**
     * The speed the user of the SensorInterface currents has.
     * This is not mandatory. A non existing speed is always Double.MIN_VALUE
     */
    private final double speed;


    /**
     * The accuracy of the location provided.
     * This is not mandatory. A non existing speed is always Double.MIN_VALUE
     */
    private final double accuracy;


    /**
     * The internal representation of a Location.
     * It consists od a Latitude, Longitude, Speed and Height.
     *
     * If speed or height are not measured or are inaccurate,
     * mark them with Double.MIN_VALUE
     *
     * @param latitude the locations latitude position on the world
     * @param longitude the locations longitude position on the world
     * @param height the height of the location relative to ocean height
     * @param speed the speed of the user
     */
    public SimpleLocation(double latitude, double longitude, double height,
                          double speed, double accuracy) {

        this.latitude = latitude;
        this.longitude = longitude;
        this.height = height;
        this.speed = speed;
        this.accuracy = accuracy;
    }


    /**
     * Generates a {@link SimpleLocation} from a Real Android SensorInterface provided Location.
     *
     * @param location to build from
     */
    public SimpleLocation(Location location){
        this.latitude = location.getLatitude();
        this.longitude = location.getLongitude();

        if(location.hasAltitude()){
            this.height = location.getAltitude();
        }else{
            this.height = Double.MIN_VALUE;
        }

        if(location.hasSpeed()){
            this.speed = location.getSpeed();
        }else{
            this.speed = Double.MIN_VALUE;
        }

        if(location.hasSpeed()){
            this.accuracy = location.getAccuracy();
        }else{
            this.accuracy = Double.MIN_VALUE;
        }
    }


    /**
     * Returns true when the Speed is present,
     * false otherwise.
     *
     * @return when present
     */
    public boolean isSpeedPresent(){
        return this.speed != Double.MIN_VALUE;
    }


    /**
     * Return true when the height is present,
     * false otherwise
     *
     * @return when present
     */
    public boolean isHeightPresent(){
        return this.height != Double.MIN_VALUE;
    }

    /**
     * Return true when the accuracy is present,
     * false otherwise
     *
     * @return when present
     */
    public boolean isAccuracyPresent(){
        return this.accuracy != Double.MIN_VALUE;
    }


    public double getLatitude() {
        return latitude;
    }

    public double getLongitude() {
        return longitude;
    }

    public double getHeight() {
        return height;
    }

    public double getSpeed() {
        return speed;
    }

    public double getAccuracy() {
        return accuracy;
    }


    @Override
    public String toString(){
        String returnString =  "latitude: '" + latitude + "' longitude: '" +longitude + "'";
        if(isAccuracyPresent()){
            returnString += " accuracy:'" + accuracy + "'";
        }

        if(isHeightPresent()){
            returnString += " height:'" + height + "'";
        }

        if(isSpeedPresent()){
            returnString += " speed:'" + speed + "'";
        }

        return returnString;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof SimpleLocation)) return false;

        SimpleLocation that = (SimpleLocation) o;

        if (Double.compare(that.accuracy, accuracy) != 0) return false;
        if (Double.compare(that.height, height) != 0) return false;
        if (Double.compare(that.latitude, latitude) != 0) return false;
        if (Double.compare(that.longitude, longitude) != 0) return false;
        if (Double.compare(that.speed, speed) != 0) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = Double.doubleToLongBits(latitude);
        result = (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(longitude);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(height);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(speed);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(accuracy);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}
